在開發的領域中,每個元件何時產生、何時建立新元件都是會影響效能,都是每個開發者會注重的議題,在前面啟章敘述過,開發者採用Spring 框架時,必定有多種模式註解會註冊進BeanFactory中,但何時才需要註冊這個Bean元件,都會影響相關整個系統效率,此時懶註冊(@Lazy)是各位開發者首要選擇,進行分析某些元件非必要性,當有需求再建立,無需所有元件都註冊進配置池進而影響效能,我們可通過原理與架構來進一步理解所該註解模式帶給開發者提升效率層的效益。
在前面章節以敘述過注入Bean方法分為兩種,一種為透過BeanFactory或子介面ApplicationContext兩項元件進行讀取XML元件進行註冊,或透過模式註解進行元件掃描註冊後,再次進行注入,第二種為透過註解進行注入,分為@Autowired或@Resource註解方式進行注入,大部分元件都是透過CommonAnnotationBeanPostProcessor此處理器元件進行過濾與獲取,那如何進行分析與過濾懶註冊(@Lazy)元件呢?可以看到透過getResourceToInject這方法中,如果是@Lazy註解,则是通過建構一個代理模式對象進行返回,否則直接調用getResource方法(如程式碼區塊一),那我們得知此註解所影響後續取得資源的效益過程有多少,提供開發者自行評估,透過我們所提供的範例建立了三個Apple、Android及BlackBerry三項服務元件,我們只有在Android服務上不配置Lazy註解,其他兩項服務皆配置此註解,相關敘述我們從下面範例程式碼可以得知。
程式法區塊一
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
配置@Lazy註解在服務上,並配置@PostConstruct註解在初始化過程中加上敘述,詳細說明如下
@Lazy
@Primary
@Service("Apple")
public class ApplePhoneServiceImpl implements PhoneServcie {
@PostConstruct
public void init() {
System.out.println("With @Lazy : ApplePhoneServiceImpl initialize. ");
}
.......
.......
.......
}
未配置@Lazy註解在服務上,並配置@PostConstruct註解在初始化過程中加上敘述,詳細說明如下
@Service("Android")
public class AndroidPhoneServiceImpl implements PhoneServcie {
@PostConstruct
public void init() {
System.out.println("Without @Lazy : AndroidPhoneServiceImpl initialize. ");
}
.....
.....
.....
}
根據PhoneTestSuite測試類別元件,可以得到相關結果,如下所示
public class PhoneTestSuite extends ServiceTestBase {
@Resource(name = "Android",type = PhoneServcie.class)
PhoneServcie androidPhoneServcie;
PhoneServcie applePhoneService = null;
@Autowired
private ApplicationContext applicationContext;
// 測試一般初始化的元件
@Test
public void testAndroidPhoneProductSizeTask() {
assertEquals(androidPhoneServcie.listAllPhone().size(),6);
System.out.println("Android vendor validate success.");
}
// 測試BlackBerry懶註冊的元件
@Test
public void testBlackBerryPhoneProductSizeTask( ) {
PhoneServcie blackBerryPhoneService = applicationContext.getBean("BlackBerry",PhoneServcie.class);
assertEquals(blackBerryPhoneService.listAllPhone().size(),2);
System.out.println("Black Berry vendor with lazy loading validate success.");
}
// 測試Apple懶註冊的元件
@Test
public void testApplePhoneProductSizeTask( ) {
PhoneServcie applePhoneService = applicationContext.getBean(PhoneServcie.class);
assertEquals(applePhoneService.listAllPhone().size(),6);
System.out.println("Apple vendor with lazy loading validate success.");
}
}
再透過運作的Log中,我們可以看到只有沒有被配置的AndroidPhoneServiceImpl類別元件有在初始化過程中被註冊,其餘兩項元件都在有需要註冊此元件時在進行註冊,相關結果如下:
2021-08-26 00:07:30.878 INFO 10772 --- [ Test worker] sw.project.sample.PhoneTestSuite : No active profile set, falling back to default profiles: default
Without @Lazy : AndroidPhoneServiceImpl initialize.
2021-08-26 00:07:32.622 INFO 10772 --- [ Test worker] sw.project.sample.PhoneTestSuite : Started PhoneTestSuite in 2.052 seconds (JVM running for 3.35)
With @Lazy : BlackBerryPhoneServiceImpl initialize.
Black Berry vendor with lazy loading validate success.
透過以上原理介紹與示範,我們可以看出如何優化我們的開發系統與流程順序是相當重要的一個步驟,開發者可以透過此篇文章作參考。
該註解原理相當簡單,就只用來判定此資源是否為懶註冊類型元件,判定是否取用代理對象進行獲取對應元件,其判定值預設為true,其註解可運用在各種類別類型、方法、建構子或宣告參數之上,讀者可透過以下圖示所得之。
圖一 @Lazy原理架構圖
Run test task
gradle test
Run open result html
open ./build/reports/tests/test/index.html
Lazy loading test
Mind-blowing test detail
Spring IOC(八)CommonAnnotationBeanPostProcessor 原理分析